#include "ecv_filter_ltd.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <unistd.h>

typedef struct
{
  int id;
  unsigned char avg;
} binbox;

G_DEFINE_TYPE (EcvFilter_Ltd, ecv_filter_ltd, ECV_TYPE_FILTER);

#define ECV_FILTER_LTD_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ECV_TYPE_FILTER_LTD, EcvFilter_LtdPrivate))

struct _EcvFilter_LtdPrivate
{
  GHashTable * lines;
  double theta1;
  double theta2;
  double ro1;
  double ro2;
};

/*enum
{
  PROP_0,
  PROP_ECV_XDIV,
  PROP_ECV_YDIV
  };*/

void 
ltd_process (EcvFilter *self)
{
  EcvFilter_Ltd * bin = ECV_FILTER_LTD(self);

  int * key1;
  dot * value1;
  int * key2;
  dot * value2;
  GHashTableIter iter1;
  GHashTableIter iter2;
  double dt=0;

  int width;
  int height;
  g_object_get (G_OBJECT (bin),"width", &width, NULL);
  g_object_get (G_OBJECT (bin),"height", &height, NULL);

  double rosqm=0;
  double rosq;
  
  g_hash_table_iter_init(&iter1, bin->priv->lines);
  g_hash_table_iter_init(&iter2, bin->priv->lines);
  int i=0;
  int j=0;
  for (i=0;i<g_hash_table_size(bin->priv->lines);i++)
    {
      value1 = g_hash_table_lookup (bin->priv->lines, &i);
      for (j=0;j<g_hash_table_size(bin->priv->lines);j++)
	{
	  value2 = g_hash_table_lookup (bin->priv->lines, &j);
	  dt = abs((double)value1->y-(double)value2->y);
	  if (dt > 40 && dt<60)
	    {
	      rosq = ((double)value1->x)*((double)value1->x)+((double)value2->x)*((double)value2->x);
	      if (rosq>rosqm)
		{
		  rosqm=rosq;
		  bin->priv->theta1 = (double)value1->y;
		  bin->priv->theta2 = (double)value2->y;
		  bin->priv->ro1 = (double)value1->x;
		  bin->priv->ro2 = (double)value2->x;
		}
	    }
	}
    }
}

void 
ltd_create (EcvFilter *self)
{
  EcvFilter_Ltd * bin = ECV_FILTER_LTD(self);
}
/*
static void
ecv_filter_ltd_set_property (GObject      *object,
                        guint         property_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
  EcvFilter_Ltd *self = ECV_FILTER_LTD (object);

  switch (property_id)
    {
    case PROP_ECV_XDIV:
      self->priv->xdiv = g_value_get_uint (value);
      break;

    case PROP_ECV_YDIV:
      self->priv->ydiv = g_value_get_uint (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
ecv_filter_ltd_get_property (GObject    *object,
                        guint       property_id,
                        GValue     *value,
                        GParamSpec *pspec)
{
  EcvFilter_Ltd *self = ECV_FILTER_LTD (object);

  switch (property_id)
    {
    case PROP_ECV_XDIV:
      g_value_set_uint (value, self->priv->xdiv);
      break;

    case PROP_ECV_YDIV:
      g_value_set_uint (value, self->priv->ydiv);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
      }
}
*/
static void
ecv_filter_ltd_dispose (GObject *gobject)
{
  EcvFilter_Ltd *self = ECV_FILTER_LTD (gobject);

  /* 
   * In dispose, you are supposed to free all types referenced from this
   * object which might themselves hold a reference to self. Generally,
   * the most simple solution is to unref all members on which you own a 
   * reference.
   */

  /* dispose might be called multiple times, so we must guard against
   * calling g_object_unref() on an invalid GObject.
   */

  /*  if (self->priv->)
    {
      g_object_unref (self->priv->an_object);
      }*/

  /* Chain up to the parent class */
  G_OBJECT_CLASS (ecv_filter_ltd_parent_class)->dispose (gobject);
}

static void
ecv_filter_ltd_finalize (GObject *gobject)
{
  EcvFilter_Ltd *self = ECV_FILTER_LTD (gobject);

  /* Chain up to the parent class */
  G_OBJECT_CLASS (ecv_filter_ltd_parent_class)->finalize (gobject);
}

static GObject *
ecv_filter_ltd_constructor (GType                  gtype,
                       guint                  n_properties,
                       GObjectConstructParam *properties)
{
  GObject *obj;

  {
    /* Always chain up to the parent constructor */
    EcvFilter_LtdClass *klass;
    GObjectClass *parent_class;  
    parent_class = G_OBJECT_CLASS (ecv_filter_ltd_parent_class);
    obj = parent_class->constructor (gtype, n_properties, properties);
  }
  
  /* update the object state depending on constructor properties */

  return obj;
}

static void
ecv_filter_ltd_class_init (EcvFilter_LtdClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  GParamSpec *pspec;

  /*  gobject_class->dispose = ecv_filter_ltd_dispose;
  gobject_class->finalize = ecv_filter_ltd_finalize;
  gobject_class->set_property = ecv_filter_ltd_set_property;
  gobject_class->get_property = ecv_filter_ltd_get_property;

  pspec = g_param_spec_uint ("xdiv",
			     "",
			     "x divider",
			     1,
			     65534,
			     4,
			     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
  
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_XDIV,
                                   pspec);

  pspec = g_param_spec_uint ("ydiv",
			     "",
			     "y divider",
			     1,
			     65534,
			     4,
			     G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_YDIV,
                                   pspec);

  */
  EcvFilterClass *parent_class;  
  parent_class = ECV_FILTER_CLASS (klass);
  parent_class->process = ltd_process;
  parent_class->create = ltd_create;
  
  g_type_class_add_private (klass, sizeof (EcvFilter_LtdPrivate));
  gobject_class->constructor = ecv_filter_ltd_constructor;
}

static void
ecv_filter_ltd_init (EcvFilter_Ltd *self)
{
  self->priv = ECV_FILTER_LTD_GET_PRIVATE (self);

  //  self->priv->an_object = g_object_new (ECV_TYPE_BA, NULL);
}

void ecv_filter_ltd_setlines(EcvFilter_Ltd *self, GHashTable * lines)
{
  g_return_if_fail (ECV_IS_FILTER_LTD (self));
  self->priv->lines = lines;
}

void ltd_line(EcvFilter_Ltd *self, int mx0, int my0, int mx1, int my1, unsigned char cr, unsigned char cg, unsigned char cb)
{
  int width;
  int height;
  unsigned char * data = ecv_filter_get_data(ECV_FILTER(self));
  g_object_get (G_OBJECT (self),"width", &width, NULL);
  g_object_get (G_OBJECT (self),"height", &height, NULL);

  double x0 = mx0;
  double y0 = my0;
  double x1 = mx1;
  double y1 = my1;

  double dx = x1 - x0;
  double dy = y1 - y0;
  
  if (fabs(dx) > fabs(dy)) 
    {          // slope < 1
      double m = dy / dx;      // compute slope
      double b = y0 - m*x0;
      dx = (dx < 0) ? -1 : 1;
      while (x0 != x1) 
	{
	  x0 += dx;
	  if (x0>=0 && x0<width && m*x0 + b>=0 && m*x0 + b<height)
	    {
	      int tx = (int)x0;
	      int ty = (int)(m*x0 + b);
	      data[(int)(tx*3+ty*width*3)]=cb;
	      data[(int)(tx*3+1+ty*width*3)]=cg;
	      data[(int)(tx*3+2+ty*width*3)]=cr;
	    }
	}
    }
  else
    {
      if (dy != 0) 
	{                              // slope >= 1
	  double m = (float) dx / (float) dy;      // compute slope
	  double b = x0 - m*y0;
	  dy = (dy < 0) ? -1 : 1;
	  while (y0 != y1) 
	    {
	      y0 += dy;
	      if (m*y0 + b>=0 && m*y0 + b<width && y0>=0 && y0<height)
		{
		  int tx = (int)(m*y0 + b);
		  int ty = (int)y0;
		  data[(int)(tx*3+ty*width*3)]=cb;
		  data[(int)(tx*3+1+ty*width*3)]=cg;
		  data[(int)(tx*3+2+ty*width*3)]=cr;
		}
            }
        }
    }
}

void ecv_filter_ltd_preparetodraw(EcvFilter_Ltd *self)
{
  g_return_if_fail (ECV_IS_FILTER_LTD (self));
  EcvFilter_Ltd * bin = self;
  //  unsigned char * data = ecv_filter_get_data(self);

  int width;
  int height;
  g_object_get (G_OBJECT (bin),"width", &width, NULL);
  g_object_get (G_OBJECT (bin),"height", &height, NULL);

  double dx2;
  double dy;
  int x2;
  int x3;
  
  double radtheta = self->priv->theta1*M_PI/180;
  dx2 = ((double)self->priv->ro1-width)/cos(radtheta);
  dy = dx2-height*sin(radtheta)/cos(radtheta);
  x2=(int)dx2;
  x3=(int)dy;
  convhough_line(self, x2, 0, x3, height-1, 0x55, 0x55, 0xFF);
  radtheta = self->priv->theta2*M_PI/180;
  dx2 = ((double)self->priv->ro2-width)/cos(radtheta);
  dy = dx2-height*sin(radtheta)/cos(radtheta);
  x2=(int)dx2;
  x3=(int)dy;
  convhough_line(self, x2, 0, x3, height-1, 0x55, 0x55, 0xFF);
}
//------------------------------------------------------
/*                     methods                        */

EcvFilter_Ltd * 
ecv_filter_ltd_new (int width, int height, int bpp)
{
  return g_object_new (ECV_TYPE_FILTER_LTD,
		       "width", width,
		       "height", height,
		       "bpp", bpp,
		       NULL);
}
